package com.hzgj.bcl.soa;

import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Queues;
import com.google.common.collect.Sets;
import com.hzgj.bcl.redis.RedisClient;
import com.hzgj.bcl.soa.monitor.MonitorService;
import com.hzgj.bcl.soa.stats.StatsData;
import com.hzgj.bcl.soa.ws.ServiceRelation;
import com.hzgj.bcl.stats.RedisKey;
import com.hzgj.bcl.stats.StatsConst;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;

/**
 * Created by Administrator on 2016/8/16.
 */
@Service
public class MonitorServiceImpl implements MonitorService {

    private Logger logger = LoggerFactory.getLogger(getClass());
    LinkedBlockingQueue<StatsData> queue = Queues.newLinkedBlockingQueue();
    @Autowired
    private RedisClient redisClient;
    @Autowired
    ServiceRelation relation;

    MonitorServiceImpl() {
        List<StatsData> cols = Lists.newArrayList();
        new Thread(() -> {
            while (!Thread.currentThread().isInterrupted()) {
                try {
                    cols.clear();
                    Queues.drain(queue, cols, 100, 1, TimeUnit.SECONDS);
                    if (cols.size() > 0) {
                        makeRedisData(cols);
                        makeServiceRelation(cols);
                    }
                } catch (Exception e) {
                    logger.error("{}", e);
                }
            }
        }).start();
    }

    @Override
    public void handle(List<StatsData> statsDatas) {
        queue.addAll(statsDatas);
    }

    Map<String, Map<String, Double>> buildSpanData(List<StatsData> statsDatas, Set<String> keys) {
        Map<String, Map<String, Double>> ret = Maps.newHashMap();
        StatsData tickData = null;
        for (StatsData statsData : statsDatas) {
            if (StatsConst.STATS_TICK_TYPE.equals(statsData.type)) {
                tickData = statsData;
            } else {
                String key = getKey(statsData);
                keys.add(key);
                if (!ret.containsKey(key)) {
                    ret.put(key, Maps.newHashMap());
                }
                ret.get(key).put(statsData.span + "_" + (statsData.success ? 1 : 0) + "_" + statsData.contextId, statsData.time + 0.0);
            }
        }
        if (tickData != null) {
            StatsData _tick = tickData;
            ret.keySet().forEach(key -> {
                ret.get(key).put(StatsConst.STATS_TICK_TYPE, _tick.time + 0.0);
            });
        }
        return ret;
    }

    void makeRedisData(List<StatsData> cols) {
        Set<String> keys = Sets.newHashSet();
        buildSpanData(cols, keys).forEach((s, members) -> redisClient.zadd(s, members));
        if (keys.size() != 0) {
            redisClient.sadd(RedisKey.SERVICE_METHOD_KEY, keys.toArray(new String[0]));
            keys.clear();
        }
    }

    void makeServiceRelation(List<StatsData> cols) {
        relation.build(cols);
    }

    private String getKey(StatsData statsData) {
        return statsData.service + "_" + statsData.method;
    }

}
